home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / dev / lang / Python16_Src.lha / Python16_Source / Modules / cgen.py < prev    next >
Encoding:
Python Source  |  2000-08-03  |  11.7 KB  |  511 lines

  1. # Python script to parse cstubs file for gl and generate C stubs.
  2. # usage: python cgen.py <cstubs >glmodule.c
  3. #
  4. # NOTE: You  must first make a python binary without the "GL" option
  5. #    before you can run this, when building Python for the first time.
  6. #    See comments in the Makefile.
  7. #
  8. # XXX BUG return arrays generate wrong code
  9. # XXX need to change error returns into gotos to free mallocked arrays
  10.  
  11.  
  12. import string
  13. import sys
  14.  
  15.  
  16. # Function to print to stderr
  17. #
  18. def err(*args):
  19.     savestdout = sys.stdout
  20.     try:
  21.         sys.stdout = sys.stderr
  22.         for i in args:
  23.             print i,
  24.         print
  25.     finally:
  26.         sys.stdout = savestdout
  27.  
  28.  
  29. # The set of digits that form a number
  30. #
  31. digits = '0123456789'
  32.  
  33.  
  34. # Function to extract a string of digits from the front of the string.
  35. # Returns the leading string of digits and the remaining string.
  36. # If no number is found, returns '' and the original string.
  37. #
  38. def getnum(s):
  39.     n = ''
  40.     while s and s[0] in digits:
  41.         n = n + s[0]
  42.         s = s[1:]
  43.     return n, s
  44.  
  45.  
  46. # Function to check if a string is a number
  47. #
  48. def isnum(s):
  49.     if not s: return 0
  50.     for c in s:
  51.         if not c in digits: return 0
  52.     return 1
  53.  
  54.  
  55. # Allowed function return types
  56. #
  57. return_types = ['void', 'short', 'long']
  58.  
  59.  
  60. # Allowed function argument types
  61. #
  62. arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double']
  63.  
  64.  
  65. # Need to classify arguments as follows
  66. #    simple input variable
  67. #    simple output variable
  68. #    input array
  69. #    output array
  70. #    input giving size of some array
  71. #
  72. # Array dimensions can be specified as follows
  73. #    constant
  74. #    argN
  75. #    constant * argN
  76. #    retval
  77. #    constant * retval
  78. #
  79. # The dimensions given as constants * something are really
  80. # arrays of points where points are 2- 3- or 4-tuples
  81. #
  82. # We have to consider three lists:
  83. #    python input arguments
  84. #    C stub arguments (in & out)
  85. #    python output arguments (really return values)
  86. #
  87. # There is a mapping from python input arguments to the input arguments
  88. # of the C stub, and a further mapping from C stub arguments to the
  89. # python return values
  90.  
  91.  
  92. # Exception raised by checkarg() and generate()
  93. #
  94. arg_error = 'bad arg'
  95.  
  96.  
  97. # Function to check one argument.
  98. # Arguments: the type and the arg "name" (really mode plus subscript).
  99. # Raises arg_error if something's wrong.
  100. # Return type, mode, factor, rest of subscript; factor and rest may be empty.
  101. #
  102. def checkarg(type, arg):
  103.     #
  104.     # Turn "char *x" into "string x".
  105.     #
  106.     if type == 'char' and arg[0] == '*':
  107.         type = 'string'
  108.         arg = arg[1:]
  109.     #
  110.     # Check that the type is supported.
  111.     #
  112.     if type not in arg_types:
  113.         raise arg_error, ('bad type', type)
  114.     if type[:2] == 'u_':
  115.         type = 'unsigned ' + type[2:]
  116.     #
  117.     # Split it in the mode (first character) and the rest.
  118.     #
  119.     mode, rest = arg[:1], arg[1:]
  120.     #
  121.     # The mode must be 's' for send (= input) or 'r' for return argument.
  122.     #
  123.     if mode not in ('r', 's'):
  124.         raise arg_error, ('bad arg mode', mode)
  125.     #
  126.     # Is it a simple argument: if so, we are done.
  127.     #
  128.     if not rest:
  129.         return type, mode, '', ''
  130.     #    
  131.     # Not a simple argument; must be an array.
  132.     # The 'rest' must be a subscript enclosed in [ and ].
  133.     # The subscript must be one of the following forms,
  134.     # otherwise we don't handle it (where N is a number):
  135.     #    N
  136.     #    argN
  137.     #    retval
  138.     #    N*argN
  139.     #    N*retval
  140.     #
  141.     if rest[:1] <> '[' or rest[-1:] <> ']':
  142.         raise arg_error, ('subscript expected', rest)
  143.     sub = rest[1:-1]
  144.     #
  145.     # Is there a leading number?
  146.     #
  147.     num, sub = getnum(sub)
  148.     if num:
  149.         # There is a leading number
  150.         if not sub:
  151.             # The subscript is just a number
  152.             return type, mode, num, ''
  153.         if sub[:1] == '*':
  154.             # There is a factor prefix
  155.             sub = sub[1:]
  156.         else:
  157.             raise arg_error, ('\'*\' expected', sub)
  158.     if sub == 'retval':
  159.         # size is retval -- must be a reply argument
  160.         if mode <> 'r':
  161.             raise arg_error, ('non-r mode with [retval]', mode)
  162.     elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])):
  163.         raise arg_error, ('bad subscript', sub)
  164.     #
  165.     return type, mode, num, sub
  166.  
  167.  
  168. # List of functions for which we have generated stubs
  169. #
  170. functions = []
  171.  
  172.  
  173. # Generate the stub for the given function, using the database of argument
  174. # information build by successive calls to checkarg()
  175. #
  176. def generate(type, func, database):
  177.     #
  178.     # Check that we can handle this case:
  179.     # no variable size reply arrays yet
  180.     #
  181.     n_in_args = 0
  182.     n_out_args = 0
  183.     #
  184.     for a_type, a_mode, a_factor, a_sub in database:
  185.         if a_mode == 's':
  186.             n_in_args = n_in_args + 1
  187.         elif a_mode == 'r':
  188.             n_out_args = n_out_args + 1
  189.         else:
  190.             # Can't happen
  191.             raise arg_error, ('bad a_mode', a_mode)
  192.         if (a_mode == 'r' and a_sub) or a_sub == 'retval':
  193.             err('Function', func, 'too complicated:',
  194.                 a_type, a_mode, a_factor, a_sub)
  195.             print '/* XXX Too complicated to generate code for */'
  196.             return
  197.     #
  198.     functions.append(func)
  199.     #
  200.     # Stub header
  201.     #
  202.     print
  203.     print 'static PyObject *'
  204.     print 'gl_' + func + '(self, args)'
  205.     print '\tPyObject *self;'
  206.     print '\tPyObject *args;'
  207.     print '{'
  208.     #
  209.     # Declare return value if any
  210.     #
  211.     if type <> 'void':
  212.         print '\t' + type, 'retval;'
  213.     #
  214.     # Declare arguments
  215.     #
  216.     for i in range(len(database)):
  217.         a_type, a_mode, a_factor, a_sub = database[i]
  218.         print '\t' + a_type,
  219.         brac = ket = ''
  220.         if a_sub and not isnum(a_sub):
  221.             if a_factor:
  222.                 brac = '('
  223.                 ket = ')'
  224.             print brac + '*',
  225.         print 'arg' + `i+1` + ket,
  226.         if a_sub and isnum(a_sub):
  227.             print '[', a_sub, ']',
  228.         if a_factor:
  229.             print '[', a_factor, ']',
  230.         print ';'
  231.     #
  232.     # Find input arguments derived from array sizes
  233.     #
  234.     for i in range(len(database)):
  235.         a_type, a_mode, a_factor, a_sub = database[i]
  236.         if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]):
  237.             # Sending a variable-length array
  238.             n = eval(a_sub[3:])
  239.             if 1 <= n <= len(database):
  240.                 b_type, b_mode, b_factor, b_sub = database[n-1]
  241.                 if b_mode == 's':
  242.                     database[n-1] = b_type, 'i', a_factor, `i`
  243.                     n_in_args = n_in_args - 1
  244.     #
  245.     # Assign argument positions in the Python argument list
  246.     #
  247.     in_pos = []
  248.     i_in = 0
  249.     for i in range(len(database)):
  250.         a_type, a_mode, a_factor, a_sub = database[i]
  251.         if a_mode == 's':
  252.             in_pos.append(i_in)
  253.             i_in = i_in + 1
  254.         else:
  255.             in_pos.append(-1)
  256.     #
  257.     # Get input arguments
  258.     #
  259.     for i in range(len(database)):
  260.         a_type, a_mode, a_factor, a_sub = database[i]
  261.         if a_type[:9] == 'unsigned ':
  262.             xtype = a_type[9:]
  263.         else:
  264.             xtype = a_type
  265.         if a_mode == 'i':
  266.             #
  267.             # Implicit argument;
  268.             # a_factor is divisor if present,
  269.             # a_sub indicates which arg (`database index`)
  270.             #
  271.             j = eval(a_sub)
  272.             print '\tif',
  273.             print '(!geti' + xtype + 'arraysize(args,',
  274.             print `n_in_args` + ',',
  275.             print `in_pos[j]` + ',',
  276.             if xtype <> a_type:
  277.                 print '('+xtype+' *)',
  278.             print '&arg' + `i+1` + '))'
  279.             print '\t\treturn NULL;'
  280.             if a_factor:
  281.                 print '\targ' + `i+1`,
  282.                 print '= arg' + `i+1`,
  283.                 print '/', a_factor + ';'
  284.         elif a_mode == 's':
  285.             if a_sub and not isnum(a_sub):
  286.                 # Allocate memory for varsize array
  287.                 print '\tif ((arg' + `i+1`, '=',
  288.                 if a_factor:
  289.                     print '('+a_type+'(*)['+a_factor+'])',
  290.                 print 'PyMem_NEW(' + a_type, ',',
  291.                 if a_factor:
  292.                     print a_factor, '*',
  293.                 print a_sub, ')) == NULL)'
  294.                 print '\t\treturn PyErr_NoMemory();'
  295.             print '\tif',
  296.             if a_factor or a_sub: # Get a fixed-size array array
  297.                 print '(!geti' + xtype + 'array(args,',
  298.                 print `n_in_args` + ',',
  299.                 print `in_pos[i]` + ',',
  300.                 if a_factor: print a_factor,
  301.                 if a_factor and a_sub: print '*',
  302.                 if a_sub: print a_sub,
  303.                 print ',',
  304.                 if (a_sub and a_factor) or xtype <> a_type:
  305.                     print '('+xtype+' *)',
  306.                 print 'arg' + `i+1` + '))'
  307.             else: # Get a simple variable
  308.                 print '(!geti' + xtype + 'arg(args,',
  309.                 print `n_in_args` + ',',
  310.                 print `in_pos[i]` + ',',
  311.                 if xtype <> a_type:
  312.                     print '('+xtype+' *)',
  313.                 print '&arg' + `i+1` + '))'
  314.             print '\t\treturn NULL;'
  315.     #
  316.     # Begin of function call
  317.     #
  318.     if type <> 'void':
  319.         print '\tretval =', func + '(',
  320.     else:
  321.         print '\t' + func + '(',
  322.     #
  323.     # Argument list
  324.     #
  325.     for i in range(len(database)):
  326.         if i > 0: print ',',
  327.         a_type, a_mode, a_factor, a_sub = database[i]
  328.         if a_mode == 'r' and not a_factor:
  329.             print '&',
  330.         print 'arg' + `i+1`,
  331.     #
  332.     # End of function call
  333.     #
  334.     print ');'
  335.     #
  336.     # Free varsize arrays
  337.     #
  338.     for i in range(len(database)):
  339.         a_type, a_mode, a_factor, a_sub = database[i]
  340.         if a_mode == 's' and a_sub and not isnum(a_sub):
  341.             print '\tPyMem_DEL(arg' + `i+1` + ');'
  342.     #
  343.     # Return
  344.     #
  345.     if n_out_args:
  346.         #
  347.         # Multiple return values -- construct a tuple
  348.         #
  349.         if type <> 'void':
  350.             n_out_args = n_out_args + 1
  351.         if n_out_args == 1:
  352.             for i in range(len(database)):
  353.                 a_type, a_mode, a_factor, a_sub = database[i]
  354.                 if a_mode == 'r':
  355.                     break
  356.             else:
  357.                 raise arg_error, 'expected r arg not found'
  358.             print '\treturn',
  359.             print mkobject(a_type, 'arg' + `i+1`) + ';'
  360.         else:
  361.             print '\t{ PyObject *v = PyTuple_New(',
  362.             print n_out_args, ');'
  363.             print '\t  if (v == NULL) return NULL;'
  364.             i_out = 0
  365.             if type <> 'void':
  366.                 print '\t  PyTuple_SetItem(v,',
  367.                 print `i_out` + ',',
  368.                 print mkobject(type, 'retval') + ');'
  369.                 i_out = i_out + 1
  370.             for i in range(len(database)):
  371.                 a_type, a_mode, a_factor, a_sub = database[i]
  372.                 if a_mode == 'r':
  373.                     print '\t  PyTuple_SetItem(v,',
  374.                     print `i_out` + ',',
  375.                     s = mkobject(a_type, 'arg' + `i+1`)
  376.                     print s + ');'
  377.                     i_out = i_out + 1
  378.             print '\t  return v;'
  379.             print '\t}'
  380.     else:
  381.         #
  382.         # Simple function return
  383.         # Return None or return value
  384.         #
  385.         if type == 'void':
  386.             print '\tPy_INCREF(Py_None);'
  387.             print '\treturn Py_None;'
  388.         else:
  389.             print '\treturn', mkobject(type, 'retval') + ';'
  390.     #
  391.     # Stub body closing brace
  392.     #
  393.     print '}'
  394.  
  395.  
  396. # Subroutine to return a function call to mknew<type>object(<arg>)
  397. #
  398. def mkobject(type, arg):
  399.     if type[:9] == 'unsigned ':
  400.         type = type[9:]
  401.         return 'mknew' + type + 'object((' + type + ') ' + arg + ')'
  402.     return 'mknew' + type + 'object(' + arg + ')'
  403.  
  404.  
  405. defined_archs = []
  406.  
  407. # usage: cgen [ -Dmach ... ] [ file ]
  408. for arg in sys.argv[1:]:
  409.     if arg[:2] == '-D':
  410.         defined_archs.append(arg[2:])
  411.     else:
  412.         # Open optional file argument
  413.         sys.stdin = open(arg, 'r')
  414.  
  415.  
  416. # Input line number
  417. lno = 0
  418.  
  419.  
  420. # Input is divided in two parts, separated by a line containing '%%'.
  421. #    <part1>        -- literally copied to stdout
  422. #    <part2>        -- stub definitions
  423.  
  424. # Variable indicating the current input part.
  425. #
  426. part = 1
  427.  
  428. # Main loop over the input
  429. #
  430. while 1:
  431.     try:
  432.         line = raw_input()
  433.     except EOFError:
  434.         break
  435.     #
  436.     lno = lno+1
  437.     words = string.split(line)
  438.     #
  439.     if part == 1:
  440.         #
  441.         # In part 1, copy everything literally
  442.         # except look for a line of just '%%'
  443.         #
  444.         if words == ['%%']:
  445.             part = part + 1
  446.         else:
  447.             #
  448.             # Look for names of manually written
  449.             # stubs: a single percent followed by the name
  450.             # of the function in Python.
  451.             # The stub name is derived by prefixing 'gl_'.
  452.             #
  453.             if words and words[0][0] == '%':
  454.                 func = words[0][1:]
  455.                 if (not func) and words[1:]:
  456.                     func = words[1]
  457.                 if func:
  458.                     functions.append(func)
  459.             else:
  460.                 print line
  461.         continue
  462.     if not words:
  463.         continue        # skip empty line
  464.     elif words[0] == 'if':
  465.         # if XXX rest
  466.         # if !XXX rest
  467.         if words[1][0] == '!':
  468.             if words[1][1:] in defined_archs:
  469.                 continue
  470.         elif words[1] not in defined_archs:
  471.             continue
  472.         words = words[2:]
  473.     if words[0] == '#include':
  474.         print line
  475.     elif words[0][:1] == '#':
  476.         pass            # ignore comment
  477.     elif words[0] not in return_types:
  478.         err('Line', lno, ': bad return type :', words[0])
  479.     elif len(words) < 2:
  480.         err('Line', lno, ': no funcname :', line)
  481.     else:
  482.         if len(words) % 2 <> 0:
  483.             err('Line', lno, ': odd argument list :', words[2:])
  484.         else:
  485.             database = []
  486.             try:
  487.                 for i in range(2, len(words), 2):
  488.                     x = checkarg(words[i], words[i+1])
  489.                     database.append(x)
  490.                 print
  491.                 print '/*',
  492.                 for w in words: print w,
  493.                 print '*/'
  494.                 generate(words[0], words[1], database)
  495.             except arg_error, msg:
  496.                 err('Line', lno, ':', msg)
  497.  
  498.  
  499. print
  500. print 'static struct PyMethodDef gl_methods[] = {'
  501. for func in functions:
  502.     print '\t{"' + func + '", gl_' + func + '},'
  503. print '\t{NULL, NULL} /* Sentinel */'
  504. print '};'
  505. print
  506. print 'void'
  507. print 'initgl()'
  508. print '{'
  509. print '\t(void) Py_InitModule("gl", gl_methods);'
  510. print '}'
  511.